package com.sugar.psych.assessment.module.business.service.usertreeholeclock;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.sugar.psych.assessment.framework.mybatis.core.dataobject.BaseDO;
import com.sugar.psych.assessment.module.business.controller.admin.userpointsrecord.vo.UserPointsRecordSaveReqVO;
import com.sugar.psych.assessment.module.business.controller.app.usertreeholeclock.vo.AppUserTreeHoleClockMonthRespVO;
import com.sugar.psych.assessment.module.business.controller.app.usertreeholeclock.vo.AppUserTreeHoleClockSaveReqVO;
import com.sugar.psych.assessment.module.business.service.userpointsrecord.UserPointsRecordService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;

import com.sugar.psych.assessment.module.business.controller.admin.usertreeholeclock.vo.*;
import com.sugar.psych.assessment.module.business.dal.dataobject.usertreeholeclock.UserTreeHoleClockDO;
import com.sugar.psych.assessment.framework.common.pojo.PageResult;
import com.sugar.psych.assessment.framework.common.pojo.PageParam;
import com.sugar.psych.assessment.framework.common.util.object.BeanUtils;

import com.sugar.psych.assessment.module.business.dal.mysql.usertreeholeclock.UserTreeHoleClockMapper;

import static com.sugar.psych.assessment.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.sugar.psych.assessment.framework.common.util.collection.CollectionUtils.convertList;
import static com.sugar.psych.assessment.framework.common.util.collection.CollectionUtils.diffList;
import static com.sugar.psych.assessment.module.business.enums.ErrorCodeConstants.*;
import static com.sugar.psych.assessment.module.business.enums.PointRecordClsEnum.CLS2;
import static com.sugar.psych.assessment.module.business.enums.PointRecordClsEnum.CLS6;

/**
 * 树洞打卡 Service 实现类
 *
 * @author Ysugar
 */
@Service
@Validated
public class UserTreeHoleClockServiceImpl implements UserTreeHoleClockService {

    @Resource
    private UserTreeHoleClockMapper userTreeHoleClockMapper;
    @Resource
    private UserPointsRecordService userPointsRecordService;

    @Override
    public Long createUserTreeHoleClock(UserTreeHoleClockSaveReqVO createReqVO) {
        // 插入
        UserTreeHoleClockDO userTreeHoleClock = BeanUtils.toBean(createReqVO, UserTreeHoleClockDO.class);
        userTreeHoleClockMapper.insert(userTreeHoleClock);
        // 返回
        return userTreeHoleClock.getId();
    }

    @Override
    public void updateUserTreeHoleClock(UserTreeHoleClockSaveReqVO updateReqVO) {
        // 校验存在
        validateUserTreeHoleClockExists(updateReqVO.getId());
        // 更新
        UserTreeHoleClockDO updateObj = BeanUtils.toBean(updateReqVO, UserTreeHoleClockDO.class);
        userTreeHoleClockMapper.updateById(updateObj);
    }

    @Override
    public void deleteUserTreeHoleClock(Long id) {
        // 校验存在
        validateUserTreeHoleClockExists(id);
        // 删除
        userTreeHoleClockMapper.deleteById(id);
    }

    @Override
    public void deleteUserTreeHoleClockListByIds(List<Long> ids) {
        // 校验存在
        validateUserTreeHoleClockExists(ids);
        // 删除
        userTreeHoleClockMapper.deleteByIds(ids);
    }

    private void validateUserTreeHoleClockExists(List<Long> ids) {
        List<UserTreeHoleClockDO> list = userTreeHoleClockMapper.selectByIds(ids);
        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
            throw exception(USER_TREE_HOLE_CLOCK_NOT_EXISTS);
        }
    }

    private void validateUserTreeHoleClockExists(Long id) {
        if (userTreeHoleClockMapper.selectById(id) == null) {
            throw exception(USER_TREE_HOLE_CLOCK_NOT_EXISTS);
        }
    }

    @Override
    public UserTreeHoleClockDO getUserTreeHoleClock(Long id) {
        return userTreeHoleClockMapper.selectById(id);
    }

    @Override
    public PageResult<UserTreeHoleClockRespVO> getUserTreeHoleClockPage(UserTreeHoleClockPageReqVO pageReqVO) {
        return userTreeHoleClockMapper.selectPage(pageReqVO);
    }

    @Override
    public Long appCreateUserTreeHoleClock(AppUserTreeHoleClockSaveReqVO createReqVO, Long loginUserId) {
        LocalDateTime nowDateTime = LocalDateTime.now().with(LocalTime.MIN);
        LocalDateTime clockTime = createReqVO.getClockTime();

        // 验证日期
        /*if (clockTime.isBefore(nowDateTime))
            throw exception(USER_TREE_HOLE_CLOCK_NOW_DATE_BEFORE);*/
        if (clockTime.isAfter(nowDateTime.with(LocalTime.MAX)))
            throw exception(USER_TREE_HOLE_CLOCK_NOW_DATE_AFTER);

        // 验证是否重复
        /*UserTreeHoleClockDO clockDO = userTreeHoleClockMapper.selectOne(Wrappers.<UserTreeHoleClockDO>lambdaQuery()
                .eq(UserTreeHoleClockDO::getUserId, loginUserId)
                .eq(UserTreeHoleClockDO::getType, createReqVO.getType())
                .eq(UserTreeHoleClockDO::getCreateDate, clockTime.toLocalDate()));
        if (Objects.nonNull(clockDO))
            throw exception(USER_TREE_HOLE_CLOCK_IS_EXISTS);*/

        // 若是起床打卡，则判断用户昨日睡觉打卡到现在是否超过 7小时，超过+3积分
        Boolean type = createReqVO.getType();
        if (type){
            LocalDateTime yesterdayStartDate = clockTime.plusDays(-1);
            UserTreeHoleClockDO lastSleepClock = userTreeHoleClockMapper.selectOne(Wrappers.<UserTreeHoleClockDO>lambdaQuery()
                    .eq(UserTreeHoleClockDO::getUserId, loginUserId)
                    .eq(UserTreeHoleClockDO::getType, 0)
                    .eq(UserTreeHoleClockDO::getCreateDate, yesterdayStartDate.toLocalDate())
                    .orderByDesc(BaseDO::getCreateTime).last("LIMIT 1"));
            if (lastSleepClock != null){
                long hour = LocalDateTimeUtil.between(lastSleepClock.getClockTime(), clockTime, ChronoUnit.HOURS);
                if (hour >= 7){
                    // 赠送用户睡眠积分
                    UserPointsRecordSaveReqVO reqVO = new UserPointsRecordSaveReqVO();
                    reqVO.setUserId(loginUserId).setCls(CLS2.getValue()).setType(true).setObtainedPoints(3).setTitle("睡眠达标 7 小时");
                    userPointsRecordService.createUserPointsRecord(reqVO);
                }
            }
        }

        UserTreeHoleClockDO clock = BeanUtils.toBean(createReqVO, UserTreeHoleClockDO.class);
        clock.setUserId(loginUserId);
        clock.setCreateDate(clockTime.toLocalDate());
        userTreeHoleClockMapper.insert(clock);

        return clock.getId();
    }

    @Override
    public List<UserTreeHoleClockRespVO> getUserTreeHoleClockByDate(String date, Long loginUserId) {
        return BeanUtils.toBean(userTreeHoleClockMapper.selectList(Wrappers.<UserTreeHoleClockDO>lambdaQuery()
                .eq(UserTreeHoleClockDO::getUserId, loginUserId)
                .eq(UserTreeHoleClockDO::getCreateDate, date)), UserTreeHoleClockRespVO.class);
    }

    @Override
    public List<AppUserTreeHoleClockMonthRespVO> getOneMonthData(String date, Long loginUserId) {
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate startDate = LocalDate.parse(date + "-01", dateFormatter);
        LocalDate endDate = startDate.with(TemporalAdjusters.lastDayOfMonth());

        List<UserTreeHoleClockDO> clocks = userTreeHoleClockMapper.selectList(Wrappers.<UserTreeHoleClockDO>lambdaQuery()
                .eq(UserTreeHoleClockDO::getUserId, loginUserId)
                .between(UserTreeHoleClockDO::getCreateDate, startDate, endDate));

        List<AppUserTreeHoleClockMonthRespVO> respVOList = new ArrayList<>();

        do {
            AppUserTreeHoleClockMonthRespVO respVO = new AppUserTreeHoleClockMonthRespVO();
            respVO.setDate(startDate.format(dateFormatter));

            LocalDate finalStartDate = startDate;
            respVO.setIsClock(clocks.stream().anyMatch(c -> c.getCreateDate().isEqual(finalStartDate)));

            startDate = startDate.plusDays(1);
            respVOList.add(respVO);
        }while (startDate.isBefore(endDate) || startDate.isEqual(endDate));

        return respVOList;
    }

}